projects/Aligned-Platform-EnergizeAI/app/src/pages/spaces/[space_id]/my-ratings.tsx (158 lines of code) (raw):
import { useState } from "react"
import { ContributionsLayout } from "@/components/contributions/layout"
import { TopicTreeMenu } from "@/components/topics/topic-tree-menu"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import QueryDataLoader from "@/components/ui/query-data-loader"
import { SearchInput } from "@/components/ui/search-input"
import { SectionHeader } from "@/components/ui/section-header"
import { Separator } from "@/components/ui/separator"
import { Skeleton } from "@/components/ui/skeleton"
import { SkeletonCard } from "@/components/ui/skeleton-card"
import { SmallSpinner } from "@/components/ui/small-spinner"
import { energizeEngine } from "@/lib/energize-engine"
import { Paths, SEARCH_PARAM_KEYS } from "@/lib/paths"
import useDebounce from "@/lib/use-debounce"
import useQueryState from "@/lib/use-query-state"
import { cn } from "@/lib/utils"
import { useRouter } from "next/router"
const MyRatingsPage = () => {
const { space_id } = useRouter().query
const [topicId, setTopicId] = useState<string | null>(null)
const [search, setSearch] = useQueryState<string | undefined>("search", {
defaultValue: undefined,
})
const searchQuery = useDebounce(search, 500)
const ratings = energizeEngine.ratings.getMyRatingsHistory.useInfiniteQuery(
{
spaceId: space_id as string,
filter: null,
topicId: topicId as string,
search: searchQuery ?? null,
limit: 10,
},
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
},
)
const ratingTagAssignments = energizeEngine.ratingTags.getAssignedTagsForRatingsMap.useQuery(
{
spaceId: space_id as string,
ratingIds: ratings.data?.pages.map((page) => page.items.map((item) => item.id)).flat() ?? [],
},
{ enabled: ratings.data !== undefined },
)
const router = useRouter()
const handleGoToGuideline = (guidelineId: string, topicId: string) => {
const url = new URL(`/spaces/${space_id}`, window.location.href)
url.searchParams.set(SEARCH_PARAM_KEYS[Paths.Playground].guidelineId, guidelineId)
url.searchParams.set(SEARCH_PARAM_KEYS[Paths.Playground].topicId, topicId)
router.push(url)
}
const cards = ratings.data
? ratings.data.pages
.map((page) => page.items)
.flat()
.map((r) => (
<Card
className="w-full cursor-pointer hover:border-primary"
key={r.id}
onClick={() => {
handleGoToGuideline(r.guidelineId, r.topic.id)
}}
>
<CardHeader className="flex flex-row justify-between">
<div className="flex flex-col gap-2">
<CardTitle>{r.topic.title}</CardTitle>
<CardDescription>Created at: {r.createdAt.toLocaleDateString()}</CardDescription>
</div>
{r.rating === "helpful" ? (
<div
className={cn(
"flex h-8 w-36 items-center justify-center rounded bg-green-100 text-sm text-xs font-medium text-success",
"dark:bg-green-900 dark:text-green-200",
)}
>
Helpful
</div>
) : (
<div
className={cn(
"flex h-8 w-36 items-center justify-center rounded bg-red-100 text-sm text-xs font-medium text-destructive",
"dark:bg-red-900 dark:text-red-200",
)}
>
Not Helpful
</div>
)}
</CardHeader>
<CardContent>
{r.guidelines.value}
<div className="mt-4 flex gap-2">
<QueryDataLoader queryResults={ratingTagAssignments} addSkeletonContainer={false} skeletonItems={1}>
<QueryDataLoader.IsSuccess>
{ratingTagAssignments.data && ratingTagAssignments.data[r.id]
? ratingTagAssignments.data[r.id].map((rt) => (
<div key={rt.id} className="flex h-8 items-center justify-center rounded bg-muted px-2">
{rt.value}
</div>
))
: null}
</QueryDataLoader.IsSuccess>
<QueryDataLoader.IsLoading>
<Skeleton className="h-8 w-20 rounded" />
<Skeleton className="h-8 w-20 rounded" />
<Skeleton className="h-8 w-20 rounded" />
</QueryDataLoader.IsLoading>
</QueryDataLoader>
</div>
</CardContent>
</Card>
))
: null
const nextButton = ratings.hasNextPage ? (
<Button
variant={"secondary"}
onClick={() => ratings.fetchNextPage()}
disabled={ratings.isLoading}
className="mx-auto w-min whitespace-nowrap"
>
Load more
{ratings.isFetchingNextPage && <SmallSpinner className="ml-2" />}
</Button>
) : null
return (
<ContributionsLayout>
<div className="flex flex-col gap-4">
<SectionHeader title="Ratings" description="Explore all the ratings you have made">
<TopicTreeMenu
currentTopicId={topicId}
handleNewTopicId={(topicId: string) => {
setTopicId(topicId)
}}
className="max-w-sm flex-1 whitespace-nowrap"
/>
</SectionHeader>
<Separator />
<SearchInput
value={search}
placeholder="Search by guideline..."
onChange={(e) => setSearch(e.target.value)}
className="w-full max-w-none"
/>
<QueryDataLoader
forceState={ratings.isSuccess && ratings.data.pages[0].items.length === 0 ? "empty" : undefined}
queryResults={ratings}
skeletonItems={4}
>
<QueryDataLoader.IsLoading>
<SkeletonCard />
</QueryDataLoader.IsLoading>
<QueryDataLoader.IsSuccess>{cards}</QueryDataLoader.IsSuccess>
</QueryDataLoader>
{nextButton}
</div>
</ContributionsLayout>
)
}
export default MyRatingsPage